package me.pinion.interceptor;

import com.jfinal.aop.Interceptor;
import com.jfinal.core.ActionInvocation;
import me.pinion.Log;
import me.pinion.check.*;
import me.pinion.check.annotation.CheckBind;
import me.pinion.kit.ClassCatch;
import me.pinion.kit.ClassKit;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class RequestCheckInterceptor implements Interceptor{

    private Map<Class, Set<Check>> checkMap = new HashMap<Class, Set<Check>>();

    public RequestCheckInterceptor(){
        ClassKit.loopClasses(new ClassCatch() {
            @Override
            public void doCatch(Class<?> clazz) {
                if (AbstractCheck.class.isAssignableFrom(clazz) && clazz != AbstractCheck.class) {

                    CheckBind bind = clazz.getAnnotation(CheckBind.class);
                    try {

                        if (checkMap.get(bind.value()) == null){
                            checkMap.put(bind.value(), new HashSet<Check>());
                        }
                        checkMap.get(bind.value()).add((Check)clazz.newInstance());
                    } catch (InstantiationException e) {
                        Log.e(e);
                    } catch (IllegalAccessException e) {
                        Log.e(e);
                    }
                }

            }
        });
    }
    @Override
    public void intercept(ActionInvocation ai) {

        if(checkAction(ai) != 0){
            return;
        }
        if(checkCtrl(ai) != 0){
            return;
        }
        ai.invoke();
    }

    private int checkAction(ActionInvocation ai){
        for(Annotation anno: ai.getMethod().getAnnotations()){
            try {
                Class annoClass = Class.forName(anno.annotationType().getName());
                Set<Check> checks = checkMap.get(annoClass);
                if (checks != null){
                    for (Check check:checks){
                        int code = check.onRequest(ai.getMethod().getAnnotation(annoClass), ai);
                        if(code != 0){
                            return code;
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
                Log.e(e);
            }
        }
        return 0;
    }

    private int checkCtrl(ActionInvocation ai){
        for(Annotation anno: ai.getController().getClass().getAnnotations()){
            try {
                Class annoClass = Class.forName(anno.annotationType().getName());
                Set<Check> checks = checkMap.get(annoClass);
                if (checks != null){
                    for (Check check:checks){
                        int code = check.onRequest(ai.getController().getClass().getAnnotation(annoClass), ai);
                        if(0 != code){
                            return code;
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
                Log.e(e);
            }
        }
        return 0;
    }

    public void addCheck(Class clazz, Check check){
        if (checkMap.get(clazz) == null){
            checkMap.put(clazz, new HashSet<Check>());
        }
        checkMap.get(clazz).add(check);
    }
}
